#include <stdlib.h>
#include <string.h>
#include <hashtbl.h>
#include "scene.h"

#define SCENE_IMPL_SRC
#include "scene_impl.h"

struct scene *scn_create(void)
{
	struct scene *s;

	if(!(s = malloc(sizeof *s))) {
		return 0;
	}
	memset(s, 0, sizeof *s);

	if(!(s->materials = htab_create(htab_hash_string, htab_cmp_string))) {
		free(s);
		return 0;
	}

	s->obj_arr_sz = 16;
	if(!(s->objects = malloc(s->obj_arr_sz * sizeof *s->objects))) {
		htab_free(s->materials);
		free(s);
		return 0;
	}
	s->obj_count = 0;

	return s;
}

void scn_free(struct scene *s)
{
	if(s->materials) {
		htab_free(s->materials);
	}
	free(s->objects);
	free(s);
}

void scn_print_info(struct scene *s, FILE *fp)
{
	int i;

	fprintf(fp, "scene: %s\n", s->name ? s->name : "unnamed");
	fprintf(fp, "  objects: %d\n", s->obj_count);

	for(i=0; i<s->obj_count; i++) {
		fputs("    ", fp);
		obj_print_info(s->objects[i], fp);
	}
	fputc('\n', fp);
}

int scn_set_name(struct scene *s, const char *name)
{
	char *tmp;

	if(!(tmp = malloc(strlen(name) + 1))) {
		return -1;
	}
	strcpy(tmp, name);

	free(s->name);
	s->name = tmp;
	return 0;
}

const char *scn_get_name(struct scene *s)
{
	return s->name;
}

/* scn_read/scn_write are at: scn_io.c */

int scn_add_material(struct scene *s, struct material *mat, const char *name)
{
	return htab_add(s->materials, (char*)name, mat);
}

struct material *scn_get_material(struct scene *s, const char *name)
{
	return htab_get(s->materials, (char*)name);
}

int scn_add_object(struct scene *s, struct object *obj)
{
	if(s->obj_count == s->obj_arr_sz) {
		void *tmp;
		int new_sz = s->obj_arr_sz * 2;

		if(!(tmp = realloc(s->objects, new_sz * sizeof *s->objects))) {
			return -1;
		}
		s->objects = tmp;
		s->obj_arr_sz = new_sz;
	}

	s->objects[s->obj_count++] = obj;
	return 0;
}

struct object *scn_get_object(struct scene *s, const char *name)
{
	int i;

	for(i=0; i<s->obj_count; i++) {
		if(strcmp(obj_get_name(s->objects[i]), name) == 0) {
			return s->objects[i];
		}
	}
	return 0;
}

int scn_object_count(struct scene *s)
{
	return s->obj_count;
}

struct object *scn_object(struct scene *s, int idx)
{
	if(idx < 0 || idx > s->obj_count) {
		return 0;
	}
	return s->objects[idx];
}

int scn_add_light(struct scene *s, struct light *lt)
{
	if(s->lt_count == s->lt_arr_sz) {
		void *tmp;
		int new_sz = s->lt_arr_sz * 2;

		if(!(tmp = realloc(s->lights, new_sz * sizeof *s->lights))) {
			return -1;
		}
		s->lights = tmp;
		s->lt_arr_sz = new_sz;
	}

	s->lights[s->lt_count++] = lt;
	return 0;
}

int scn_light_count(struct scene *s)
{
	return s->lt_count;
}

struct light *scn_light(struct scene *s, int idx)
{
	if(idx < 0 || idx > s->lt_count) {
		return 0;
	}
	return s->lights[idx];
}
